package com.base.baseweb;

import com.alibaba.fastjson2.JSONObject;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import java.text.DecimalFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;

public abstract class FormatFetchPostProcessorFactory<C> implements FetchPostProcessorFactory<C>{

    @Component
    public static class DateFormatFetchPostProcessorFactory extends FormatFetchPostProcessorFactory<DateFormatFetchPostProcessorFactory.Config> {
        @Override
        public DateFormatFetchPostProcessorFactory.Config resolve(Map<String, Object> config) {
            DateFormatFetchPostProcessorFactory.Config conf = new DateFormatFetchPostProcessorFactory.Config();
            conf.field = (String) config.get("field");
            conf.format = (String) config.get("format");
            conf.formatter = DateTimeFormatter.ofPattern(conf.format, Locale.getDefault());
            return conf;
        }

        @Override
        public FetchPostProcessor apply(Config config) {
            return new FetchPostProcessor() {
                @Override
                public void process(List<JSONObject> in, VariableHolder<List<JSONObject>> out, FetchPostProcessorChain chain) {
                    List<JSONObject> list = new ArrayList<>();
                    for (JSONObject obj : in) {
                        JSONObject o = new JSONObject(obj);
                        list.add(o);
                        for (Map.Entry<String, Object> entry : o.entrySet()) {
                            if (StringUtils.equals(entry.getKey(), config.field)) {
                                String formatValue;
                                if (entry.getValue() instanceof LocalDate) {
                                    formatValue = ((LocalDate) entry.getValue()).format(config.formatter);
                                }else if (entry.getValue() instanceof LocalDateTime){
                                    formatValue = ((LocalDateTime) entry.getValue()).format(config.formatter);
                                } else if (entry.getValue() instanceof Date) {
                                    formatValue = ((Date) entry.getValue()).toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime().format(config.formatter);
                                } else {
                                    formatValue = entry.getValue().toString();
                                }
                                entry.setValue(formatValue);
                            }
                        }
                    }
                    out.value = list;

                    chain.doPostProcess(out.value ,out);
                }
            };
        }

        @Data
        public static class Config {
            private String field;
            private String format;
            private DateTimeFormatter formatter;
        }
    }

    @Component
    public static class NumberFormatFetchPostProcessorFactory extends FormatFetchPostProcessorFactory<NumberFormatFetchPostProcessorFactory.Config> {

        @Override
        public Config resolve(Map<String, Object> config) {
            NumberFormatFetchPostProcessorFactory.Config conf = new NumberFormatFetchPostProcessorFactory.Config();
            conf.type = (String) config.get("type");
            conf.format = (String) config.get("format");
            conf.decimalFormat = new DecimalFormat(conf.format);
            return conf;
        }

        @Override
        public FetchPostProcessor apply(Config config) {
            return new FetchPostProcessor() {
                @Override
                public void process(List<JSONObject> in, VariableHolder<List<JSONObject>> out, FetchPostProcessorChain chain) {
                    List<JSONObject> list = new ArrayList<>();
                    for (JSONObject obj : in) {
                        JSONObject o = new JSONObject(obj);
                        list.add(o);
                        for (Map.Entry<String, Object> entry : o.entrySet()) {
                            if (checkType(entry.getValue(), config.type)) {
                                entry.setValue(config.decimalFormat.format(entry.getValue()));
                            }
                        }
                    }
                    out.value = list;

                    chain.doPostProcess(out.value ,out);
                }

                public boolean checkType(Object value, String type){
                    if (value == null){
                        return false;
                    }
                    // 转小写
                    String _type = value.getClass().getSimpleName().toLowerCase(Locale.getDefault());
                    return value instanceof Number && _type.equals(type.toLowerCase(Locale.getDefault()));
                }
            };
        }

        @Data
        public static class Config{
            private String type;
            private String format;
            private DecimalFormat decimalFormat;
        }
    }
}
